package vacuum.noSpam;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;

import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.event.player.PlayerQuitEvent;

public class SpamDetector implements Listener
{

	Hashtable<String, ArrayList<Message>> messages = new Hashtable<String, ArrayList<Message>>();

	ArrayList<String> muted;

	private Dictionary dict;

	private StatsAnalyzer stats;

	private int testCount = 0;

	/**
	 * 0: Check vs dictionary
	 * 1: check word size
	 * 2: check character frequency
	 * 3: Check time since last
	 * 4: Distance check
	 */
	private boolean[] testsOptions;

	private float[] pointValues;

	private float maxPoints;

	private boolean verbose;

	private long maxTime;

	private int warningPoints;

	private String warningMessage;

	private String muteMessage;

	//FIXME: this is a mess :/
	public SpamDetector(ArrayList<String> muted, boolean verbose, boolean[] testsOptions, float[] pointValues, float maxPoints, long maxTime, Dictionary dict, StatsAnalyzer stats, String warningMessage, int warningPoints, String muteMessage){

		this.muteMessage = muteMessage;
		
		this.warningMessage = warningMessage;
		this.warningPoints = warningPoints;

		this.testsOptions = testsOptions;
		this.maxPoints = maxPoints;
		this.pointValues = pointValues;

		this.verbose = verbose;

		this.maxTime = maxTime;

		this.stats = stats;

		//add muted players
		this.muted = muted;

		this.dict = dict;

		for(boolean b : testsOptions){
			if(b)
				testCount++;
		}
	}

	public void addPlayer(String player){
		messages.put(player.toLowerCase(), new ArrayList<Message>());
	}

	public void removePlayer(String player){
		messages.remove(player.toLowerCase());
	}



	@EventHandler(priority=EventPriority.MONITOR)
	public void onPlayerQuit(PlayerQuitEvent event)
	{
		messages.remove(event.getPlayer().getName());
	}



	@EventHandler(priority=EventPriority.HIGH) //HIGH so that we allow other plugins that may want to override do so, but also allow us to override less important plugins
	public void onPlayerChat(PlayerChatEvent event)
	{
		log("Intercepted message \"" + event.getMessage() + "\", sent by " + event.getPlayer().getName());
		if(muted.contains(event.getPlayer().getName().toLowerCase())){
			event.setCancelled(true);
			return;
		} else if (!messages.containsKey(event.getPlayer().getName().toLowerCase())){
			//we're not supposed to be monitoring this person
			return;
		}

		float[] tests = new float[testCount];

		String message = event.getMessage();

		int i = 0;

		if(testsOptions[0]){
			tests[i++] = dict.checkMatches(message);
			log("Giving test 0 " + tests[i - 1] + " points");
		}
		if(testsOptions[1]){
			tests[i++] = stats.wordSize(message);
			log("Giving test 1 " + tests[i - 1] + " points");
		}
		if(testsOptions[2]){
			tests[i++] = stats.characterFrequency(message);
			log("Giving test 2 " + tests[i - 1] + " points");
		}

		i = 0;

		messages.get(event.getPlayer().getName().toLowerCase()).add(new Message(message, System.currentTimeMillis(), tests));

		ArrayList<Message> k = messages.get(event.getPlayer().getName().toLowerCase());

		Message last = null;
		
		for (int j = 0; j < k.size(); j++)
		{
			if(System.currentTimeMillis() - k.get(j).getTime() > maxTime){
				log("Removing " + k.get(j).getValue());
				k.remove(j--);
			}
		}

		float points = 0;
		for (Message m : k)
		{
			log("Testing message \"" + m.getValue() + "\" at time " + m.getTime());
			if(last != null && testsOptions[3]){ //time since last
				float incr = (float)m.getValue().length()/(m.getTime() - last.getTime()) * pointValues[3];
				log("Giving " + incr + " points for time.");
				points += incr;
			}

			if(testsOptions[4]) //distance from all
				for(int j = i + 1; j < k.size(); j++){
					float incr = (1 - stats.compareMessages(m, k.get(j))) * pointValues[4];
					log("Giving " + incr + " points for distance.");
					points += incr;
				}

			i = 0;

			if(testsOptions[0]){ //dictionary
				float incr = (1 - m.getTests()[i++]) * pointValues[0];
				log("Giving " + incr + " points on dictionary.");
				points += incr;
			}
			if(testsOptions[1]){ //word size
				float incr = m.getTests()[i++] * pointValues[1];
				log("Giving " + incr + " points on word size.");
				points += incr;
			}
			if(testsOptions[2]){ //character freq
				float incr = m.getTests()[i++] * pointValues[2];
				log("Giving " + incr + " points on character frequency.");
				points += incr;
			}


			last = m;
		}
		log(event.getPlayer().getName() + " earned " + points);
		if(points > maxPoints){
			System.out.println("[NoSpam] Muting " + event.getPlayer().getName());

			muted.add(event.getPlayer().getName().toLowerCase());
			event.getPlayer().sendMessage(muteMessage);
			event.setCancelled(true);
			return;
		} else if (points > warningPoints){
			System.out.println("[NoSpam] Warning " + event.getPlayer().getName());
			
			event.getPlayer().sendMessage(warningMessage);
			return;
		}

	}

	private void log(String s){
		if(verbose){
			System.out.println("[NoSpam]->[SpamDetector]" + s);
		}
	}

	public void shutdown(File dataFolder)
	{
		try{
			File muted = new File(dataFolder.toString() + File.separatorChar + "muted.txt");
			FileWriter fw = new FileWriter(muted, false);
			for(String str : this.muted){
				fw.write(str);
			}
		} catch (IOException ex){
			ex.printStackTrace();
		}
	}

}
